package module1.main;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;

/**
 * Class is designed for input two numbers from the keyboard and
 * division of integers in a column.
 *
 * @author Dranik Vladimir
 */
public class LongDivision {
    private static final String SEPARATOR = " | ";
    private static final String MESSAGE_QUOTIENT = "Quotient: ";
    private static final String MESSAGE_MODULO = "Modulo: ";
    private static final String MESSAGE_INPUT_DIVIDEND = "Please, enter a dividend: ";
    private static final String MESSAGE_INPUT_DIVISOR = "Please, enter a divisor: ";
    private static final String ERROR_INPUT_INVALID_VALUES = "You entered invalid values.\nPlease, try again.";
    private static final String ERROR_DIVISION_BY_0 = "You can not divide by 0!\nPlease, try again.";
    
    private int dividend;
    private int divisor;
    private int bitShift = 0;
    private int dividendShortened = Integer.MAX_VALUE;
    private int partOfTheNewDividend;

    public static void main(String[] args) {
	LongDivision ld = LongDivision.input();
	List<Integer> divisionResults = ld.doDivision();
	ld.print(divisionResults);
	ld.printQuotientAndModulo();
    }

    /**
     * Initializes a LongDivision object
     */  
    private LongDivision() {
    }

    /**
     * Getting data from the console and return LongDivision object. 
     * Print messages will be as long as the user does not enter the correct values.
     * 
     * @return LongDivision object
     */
    public static LongDivision input() {	
	LongDivision ld = null;
	boolean isInputErrors = true;
	while (isInputErrors) {
	    Scanner sc = new Scanner(System.in);
	    ld = new LongDivision();
	    try {
		System.out.print(MESSAGE_INPUT_DIVIDEND);
		ld.setDividend(new Integer(sc.nextInt()));
	
		System.out.print(MESSAGE_INPUT_DIVISOR);
		ld.setDivisor(new Integer(sc.nextInt()));
	
		if (ld.getDivisor() == 0) {
		    System.out.println(ERROR_DIVISION_BY_0);
		    continue;
		}
	    } catch (InputMismatchException ex) {
		System.out.println(ERROR_INPUT_INVALID_VALUES);
		continue;
	    }
	    isInputErrors = false;
	}
	return ld;
    }

    private int getQuotient() {
	return dividend / divisor;
    }

    private int getModulo() {
	return dividend % divisor;
    }

    private StringBuilder getIndentation(int quantity, String sign) {
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < quantity; i++) {
	    sb.append(sign);
	}
	return sb;
    }

    private int getPartialQuotient(int dividendShortened) {
	return dividendShortened - (dividendShortened % divisor);
    }

    private int getPartOfTheNewDividend(int dividendShortened, int partialQuotient) {
	return dividendShortened - partialQuotient;
    }

    private int getDividendShortened(int partOfTheNewDividend) {
	int dividendLenght = String.valueOf(dividend).length();
	int dividendShortened = 0;
	int i;
	for (i = 1; i <= dividendLenght - bitShift; i++) {
	    dividendShortened = formationDividendShortened(partOfTheNewDividend, i);
	    if (dividendShortened >= divisor) {
		break;
	    }
	}
	bitShift += i;
	return dividendShortened;
    }

    private int formationDividendShortened(int partOfTheNewDividend, int iterator) {
	String dividendStr = String.valueOf(dividend);
	String dividendShortenedStr = "";
	if (partOfTheNewDividend == 0) {
	    dividendShortenedStr = dividendStr.substring(bitShift, bitShift + iterator);
	} else {
	    dividendShortenedStr = partOfTheNewDividend + dividendStr.substring(bitShift, bitShift + iterator);
	}
	return Integer.valueOf(dividendShortenedStr);
    }

    
    /**
     * Allocates part in the dividend, that can be divided by divisor,
     * performs division, writes intermediate results and calculates
     * the quotient.
     * 
     * @return List<Integer> - division results
     */
    public List<Integer> doDivision() {
	List<Integer> divisionResults = new ArrayList<Integer>();
	while (dividendShortened >= divisor) {
	    dividendShortened = getDividendShortened(partOfTheNewDividend);
	    int partialQuotient = getPartialQuotient(dividendShortened);
	    partOfTheNewDividend = getPartOfTheNewDividend(dividendShortened, partialQuotient);
	    if (dividendShortened != 0 && partialQuotient != 0) {  
		divisionResults.add(dividendShortened);
		divisionResults.add(partialQuotient);
	    }
	}
	return divisionResults;
    }

    
    /**
     * Print the results of the division
     * 
     * @param divisionResults - List<Integer>, division results
     */
    public void print(List<Integer> divisionResults) {
	int dividendLenght = String.valueOf(dividend).length();
	System.out.printf("%d%s%d\n", dividend, SEPARATOR, divisor);
	int printShift = 0;
	for (int i = 0; i < divisionResults.size() - 1; i++) {
	    if (i == 0) {
		System.out.printf("%s%s%s\n", getIndentation(dividendLenght, " "),
			                      SEPARATOR,
			                      getIndentation(String.valueOf(getQuotient()).length(), "-"));
		continue;
	    }

	    if (i == 1) {
		printShift = String.valueOf(divisionResults.get(i - 1)).length() - String.valueOf(divisionResults.get(i)).length();
		System.out.printf("%s%d%s%s%d\n", getIndentation(printShift, " "),
					          divisionResults.get(i),
					          getIndentation(dividendLenght	- String.valueOf(divisionResults.get(i - 1)).length(), " "), 
					          SEPARATOR,
					          getQuotient());
		System.out.printf("%s\n", getIndentation(String.valueOf(divisionResults.get(i - 1)).length(), "-"));
		continue;
	    }
	    ++printShift;
	    System.out.printf("%s%d\n", getIndentation(printShift, " "), divisionResults.get(i));

	    if (i < divisionResults.size()) {
		System.out.printf("%s%d\n", getIndentation(printShift, " "), divisionResults.get(++i));
		System.out.printf("%s%s\n", getIndentation(printShift, " "),
				            getIndentation(String.valueOf(divisionResults.get(i - 1)).length(), "-"));
	    }
	}

	if (printShift < dividendLenght - 1) {
	    ++printShift;
	}
	System.out.printf("%s%d\n", getIndentation(printShift, " "), getModulo());
    }

    /**
     * Print quotient and modulo 
     */
    public void printQuotientAndModulo() {
	System.out.println(MESSAGE_QUOTIENT + getQuotient());
	System.out.println(MESSAGE_MODULO + getModulo());
    }

    public int getDividend() {
        return dividend;
    }

    public int getDivisor() {
        return divisor;
    }

    private void setDividend(int dividend) {
        this.dividend = dividend;
    }

    private void setDivisor(int divisor) {
        this.divisor = divisor;
    }
}
